#include <SPI.h>
#include <LPD8806.h>
#include <PinDebouncer.h>
#include <PushButton.h>
#include <LEDStripController.h>
#include <HardwareSerial.h>
#include <SerialProtocol.h>

// The pin to which the push button (or something
// equivalent) is connected (in this case it's the
// output of the capacitive sensor/switch)
int globalPushButtonPin = 8;
// The pin which enables the push button (in this
// particular example it enables/powers up the
// capacitive sensor)
int globalPushButtonEnablePin = 12;

// The PushButton object (to detect various activities like
// a single push, constant push, double push, etc.)
PushButton
  globalPB(globalPushButtonPin);

// Configuration of the PinDebouncer:
//
// Minimum time between two samples of the pin input (in
// this particular case it works around the fact that the
// capacitive sensor IC puts its output into a
// high-impedance state every so often for 15 us (IC's
// heart-beat feature))
unsigned long globalMinimumTimeBetweenDebounceSamplesUs = 20;
// Length of the debounce filter array (holding samples
// of the input pin)
const unsigned int globalDebounceFilterBufferArraySize = 8;
// The data (filter) array to be used by the debouncer (FIFO)
unsigned char globalDebounceFilterBufferArray[globalDebounceFilterBufferArraySize];

// The PinDebouncer object for filtering out higher frequencies (
// several kHz -> us)
PinDebouncer
  globalPD(
    globalPushButtonPin,
    globalMinimumTimeBetweenDebounceSamplesUs,
    globalDebounceFilterBufferArray,
    globalDebounceFilterBufferArraySize);

// Pin an LED is connected to (for monitor/debug purposes)
int globalLEDPin = 13;

// Configuration of the LED strip

int globalLEDStripDataPin = 2;
int globalLEDStripClockPin = 3;
LPD8806 globalLEDStrip(
    46,
    globalLEDStripDataPin,
    globalLEDStripClockPin);

LEDStripController globalLEDStripController(
		globalLEDStrip);

LEDStripController::SwitchType globalLEDSCCurrentSwitchType =
		LEDStripController::ST_PLAIN;

SerialProtocol globalSerialProtocol(Serial);

void Blink(unsigned long intervalMs);

unsigned char OnDigitalRead(unsigned int pinNumber, void* object)
{
  if (object)
  {
    return (static_cast<PinDebouncer*>(object))->GetPinState();
  }
  else
  {
    return digitalRead(pinNumber);
  }
}

void setup()
{
  pinMode(globalPushButtonEnablePin, OUTPUT);
  digitalWrite(globalPushButtonEnablePin, HIGH);

  pinMode(globalLEDPin, OUTPUT);
  
  digitalWrite(globalLEDPin, HIGH);
  delay(250);
  digitalWrite(globalLEDPin, LOW);
  delay(250);
  digitalWrite(globalLEDPin, HIGH);
  delay(250);
  digitalWrite(globalLEDPin, LOW);
  
  globalPB.OnDigitalRead(OnDigitalRead, &globalPD);
  
  // Start up the LED strip
  globalLEDStrip.begin();

  // Update the strip, to start they are all 'off'
  globalLEDStrip.show();

  Serial.begin(19200);
}

void loop()
{
    globalPD.Service();

    switch(globalPB.Service())
    {
      case PushButton::SINGLE_PUSH:
        {
          digitalWrite(globalLEDPin, digitalRead(globalLEDPin) == HIGH ? LOW : HIGH);
          delay(200);
          digitalWrite(globalLEDPin, digitalRead(globalLEDPin) == HIGH ? LOW : HIGH);
          globalLEDStripController.Switch(globalLEDSCCurrentSwitchType);
        }
        break;
      case PushButton::DOUBLE_PUSH:
        {
          digitalWrite(globalLEDPin, digitalRead(globalLEDPin) == HIGH ? LOW : HIGH);
          globalLEDSCCurrentSwitchType =
        		  (LEDStripController::SwitchType)((globalLEDSCCurrentSwitchType + 1) % LEDStripController::ST_COUNT);
        }
        break;
      case PushButton::CONSTANT_PUSH:
        {
            Blink(1000);
            globalLEDStripController.Dim(true);
        }
        break;
      case PushButton::CONSTANT_PUSH2:
        {
            Blink(250);
            globalLEDStripController.Dim(false);
        }
        break;
      default:
        {
          // digitalWrite(globalLEDPin, LOW);
        }
        break;
    }

    CRS232ProtocolPacket* _packet =
    		globalSerialProtocol.Service();

    if (_packet != 0x0)
    {
    	switch (_packet->DecodeCommand())
    	{
			case SWITCH_ONOFF:
				{
					globalLEDStripController.Switch(globalLEDSCCurrentSwitchType);
				}
				break;
			default:
				{
					_packet->EncodeCommand(NOT_IMPLEMENTED);
				}
			break;
    	}

    	globalSerialProtocol.SendPacket(_packet);
    }
}

unsigned long globalDelayCounterMs = 0;

void Blink(unsigned long intervalMs)
{
  {
    unsigned long _currentCounterMs = millis();
    
    if ((_currentCounterMs - globalDelayCounterMs) > intervalMs)
    {
      digitalWrite(globalLEDPin, digitalRead(globalLEDPin) == HIGH ? LOW : HIGH);
      globalDelayCounterMs = _currentCounterMs;
    }
  }
}
